import os
import datetime
import time
import json
import uuid
import requests
import hashlib

from django.conf import settings
from rest_framework import exceptions
from rest_framework.fields import DateTimeField
from bs4 import BeautifulSoup

from connection.models import Connection, Certificate, Partner
from fs.service import file_system_service
from libs.enums import PartnerType, Authorizer, ConnectionState
from .certificate import certificate_service
from datacenter.services import dataware_service
from system.models import Menu


class ConnectionService:
    def get_or_create_org_partner(self, name):
        instance, _ = Partner.objects.get_or_create(
            type=PartnerType.ORG.value,
            name=name,
        )
        return instance

    def get_all_ongoing_connections(self):
        return Connection.objects.filter(state=ConnectionState.ONGOING.value)

    def create_app_parnter(self, app_server, system_app_key=None):
        if Partner.objects.filter(type=PartnerType.APP.value, app_server=app_server).exists():
            raise exceptions.ValidationError({'error': '应用已存在'})
        try:
            api = f'{app_server}/openapi/app/info'
            response = requests.get(api)
        except Exception:
            raise exceptions.ValidationError({'error': '请检查接口地址是否正确'})
        if response.status_code != 200:
            raise exceptions.ValidationError({'error': '请检查接口地址是否正确'})
        data = json.loads(response.content)
        app = Partner.objects.create(
            type=PartnerType.APP.value,
            app_server=app_server,
            name=data.get('name'),
            detail=data,
            system_app_key=system_app_key
        )
        max_order_record = Menu.objects.filter(parent_id=13).order_by('-orderNo').first()
        orderNo = 200
        if max_order_record:
            orderNo = max_order_record.orderNo + 100
        Menu.objects.create(
            title=app.name,
            orderNo=orderNo,
            path=f'/app/{app.id}',
            component='wex/app/AppPage.vue',
            parent_id=13,
            meta={'ignoreRoute': True}
        )
        return app

    def create_connection(self, partner, state=None, start_time=None, end_time=None, thirdparty_app=None, thirdparty_id=None):
        if start_time:
            start_time = DateTimeField().to_internal_value(start_time).replace(hour=0, minute=0, second=0, microsecond=0)
        else:
            start_time = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
        if end_time:
            end_time = DateTimeField().to_internal_value(end_time).replace(hour=23, minute=59, second=59, microsecond=999999)
        else:
            end_time = start_time + datetime.timedelta(days=365, milliseconds=-1)
        instance = Connection.objects.create(
            partner_id=partner.id,
            state=state,
            start_time=start_time,
            end_time=end_time,
            thirdparty_app=thirdparty_app,
            thirdparty_id=thirdparty_id
        )
        return instance

    def get_connection_by_id(self, id):
        return Connection.objects.filter(id=id).first()

    def get_connection_by_partner_id(self, partner_id):
        return Connection.objects.filter(
            partner_id=partner_id
        ).first()

    def get_connection_by_thirdparty_id(self, thirdparty_app, thirdparty_id):
        return Connection.objects.filter(
            thirdparty_app=thirdparty_app,
            thirdparty_id=thirdparty_id
        ).first()

    def exists_partner(self, partner_id):
        return Certificate.objects.filter(
            partner_id=partner_id,
            state__in=[ConnectionState.DRAFT.value, ConnectionState.ONGOING.value]
        ).exists()

    def exists_thirdparty_id(self, thirdparty_app, thirdparty_id):
        return Connection.objects.filter(
            thirdparty_app=thirdparty_app,
            thirdparty_id=thirdparty_id
        ).exists()

    def get_our_side_certificate(self, connection_id):
        return Certificate.objects.filter(
            connection_id=connection_id,
            authorizer=Authorizer.OUR_SIDE.value,
            is_voided=False
        ).first()

    def get_other_side_certificate(self, connection_id):
        return Certificate.objects.filter(
            connection=connection_id,
            authorizer=Authorizer.OTHER_SIDE.value,
            is_voided=False
        ).first()

    def generate_certificate(self, connection, source_data, server):
        certificate_data = certificate_service.generate_certificate_data(connection, source_data, server)
        type_desc = PartnerType.get_enum(connection.partner.type).desc
        holder, provider, seq = certificate_data['holder'], certificate_data['provider'], int(time.time())
        file_name = f'{type_desc}授权书-{provider}-to-{holder}-{seq}.certx'
        relative_dir = os.path.join('certificates', str(connection.partner_id))
        url = file_system_service.save_dict_content(certificate_data, relative_dir=relative_dir, file_name=file_name)
        certificate = self._handle_save_certificate(connection, certificate_data, url, Authorizer.OUR_SIDE.value, source_data)
        if connection.partner.type == PartnerType.ORG.value and connection.thirdparty_app:
            self.push_certificate_to_app(connection)
        return certificate

    def upload_certificate(self, connection, file_object):
        ''' 上传对方授权书文件到系统
        '''
        certificate_data = json.load(file_object)
        certificate_service.check_certificate(certificate_data)
        file_name = f'{str(uuid.uuid4())}.certx'
        relative_dir = os.path.join('certificates', str(connection.partner_id))
        url = file_system_service.save(relative_dir, file_name, file_object.read())
        certificate = self._handle_save_certificate(connection, certificate_data, url, Authorizer.OTHER_SIDE.value)
        return certificate

    def import_certificate(self, connection, certificate_url):
        ''' 从平台端自动导入对方授权书文件到系统
        '''
        response = requests.get(certificate_url)
        certificate_data = json.loads(response.content)
        certificate_service.check_certificate(certificate_data)
        file_name = f'{str(uuid.uuid4())}.certx'
        relative_dir = os.path.join('certificates', str(connection.partner_id))
        url = file_system_service.save_dict_content(certificate_data, relative_dir=relative_dir, file_name=file_name)
        certificate = self._handle_save_certificate(connection, certificate_data, url, Authorizer.OTHER_SIDE.value)
        return certificate

    def _handle_save_certificate(self, connection, certificate_data, certificate_url, authorizer, source_data=None):
        validated = False
        if authorizer == Authorizer.OTHER_SIDE.value:
            validated = certificate_service.validate_certificate(certificate_data)
        certificate = Certificate.objects.create(
            connection_id=connection.id,
            url=certificate_url,
            source_data=source_data,
            data=certificate_data,
            start_time=connection.start_time,
            end_time=connection.end_time,
            server=certificate_data.get('server'),
            public_key=certificate_data.get('public_key'),
            second_key=certificate_data.get('second_key'),
            holder_open_id=certificate_data.get('holder_open_id'),
            authorizer=authorizer,
            validated=validated
        )
        if authorizer == Authorizer.OUR_SIDE.value:
            validated = certificate_service.validate_certificate(certificate_data)
            if validated:
                certificate.validated = validated
                certificate.save()
        dataware_service.init_tables(certificate)
        return certificate

    def push_certificate_to_app(self, connection):
        certificate = self.get_our_side_certificate(connection.id)
        if not certificate:
            raise exceptions.ValidationError({'error': '还未生成授权书'})

        server, headers = self.get_server_and_headers_by_partner_id(connection.thirdparty_app)
        api = f'{server}/openapi/connection/connections/certificates/import'
        data = {
            'connection_id': connection.thirdparty_id,
            'certificate_url': certificate.url
        }
        response = requests.post(api, data, headers=headers)
        if response.status_code != 200:
            raise exceptions.ValidationError({'error': '第三方应用可能存在异常，请稍后再试'})

    def validate_certificate(self, certificate_id):
        ''' 检验授权书是否能够访问对方服务
        '''
        certificate = Certificate.objects.filter(id=certificate_id).first()
        if not certificate:
            return False
        validated = certificate_service.validate_certificate(certificate.data)
        certificate.validated = validated
        certificate.save()
        return validated

    def get_filings(self, certificate_id):
        ''' 查询备案信息
        '''
        data = {
            'name': None,
            'url': None
        }
        certificate = Certificate.objects.filter(id=certificate_id).first()
        if not certificate:
            return data
        provider_filings_id = certificate.data.get('provider_filings_id')
        if not provider_filings_id:
            return data
        public_key = certificate.data.get('public_key')
        file_name = f'{hashlib.md5(public_key.encode()).hexdigest()}.html'
        url = '/'.join([settings.WWEXX_SERVER, 'media', provider_filings_id, file_name])
        response = requests.get(url)
        if response.status_code != 200:
            return data
        soup = BeautifulSoup(response.content, 'html.parser')
        name_item = soup.find(id="name")
        name = name_item.text
        data = {
            'name': name,
            'url': url
        }
        return data

    def get_server_and_headers_by_partner_id(self, partner_id):
        connection = self.get_connection_by_partner_id(partner_id)
        if not connection:
            raise exceptions.ValidationError({'error': '请先与应用建立互联'})
        certificate = self.get_other_side_certificate(connection)
        if not certificate:
            raise exceptions.ValidationError({'error': '请先与应用建立互联'})
        return certificate_service.get_server_and_headers(certificate.data)


connection_service = ConnectionService()
